package com.fin.zw.aiqas.service;

import com.fin.zw.aiqas.entity.BaseEntity;
import com.fin.zw.aiqas.model.session.SessionModel;
import com.fin.zw.aiqas.repository.BaseRepository;
import com.fin.zw.aiqas.util.BadRequestException;
import com.querydsl.core.BooleanBuilder;
import com.querydsl.core.types.dsl.BooleanExpression;
import org.apache.commons.collections4.IterableUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;


public  class BaseService<T> {

    protected final static Logger log = LoggerFactory.getLogger(BaseService.class);

    @Lazy
    @Autowired
    protected BaseRepository<T> repository;

    @Autowired
    protected CommonService commonService;


    public BooleanBuilder generateCommenCondition(Object query, Object condition) {
        BooleanBuilder builder = new BooleanBuilder();
        try {
            Class cls = condition.getClass();
            Field[] fields = cls.getDeclaredFields();

            for (Field f : fields) {
                f.setAccessible(true);
                Object v = f.get(condition);
                if (!f.equals(null) && v != null && !("".equals(v.toString()))) {
                    Field cfield = query.getClass().getDeclaredField(f.getName());
                    Method ms[] = cfield.getType().getMethods();
                    for (Method m : ms) {
                        if (m.getName() == "contains") {
                            BooleanExpression be = (BooleanExpression) m.invoke(cfield.get(query), v.toString());
                            builder.and(be);
                            break;
                        }
                    }

                }
            }
        } catch (Exception ex) {
            log.error(ex.getMessage());
            ex.printStackTrace();
            //todo记录错误日志
        }
        return builder;
    }


    protected Class<T> getGenericClass(){
        ParameterizedType ptype = (ParameterizedType)this.getClass().getGenericSuperclass();
        Class clazz = (Class<T>) (ptype.getActualTypeArguments()[0]);
        return clazz;
    }

    protected T generateEntity(BaseEntity newEntity, SessionModel sessionModel) {
        try {
            T obj = (T) this.getGenericClass().newInstance();
            BeanUtils.copyProperties(newEntity, obj);
            return obj;
        } catch (Exception ae) {
            log.error(ae.getMessage());
        }
        return null;
    }

    public List<T> fetch(SessionModel sessionModel, T condition, Object query){
        List<T> baseList = new ArrayList<>();
        BooleanBuilder builder = this.generateCommenCondition(query, condition);
        Iterable<T> iterable  = this.repository.findAll(builder);
        return IterableUtils.toList(iterable);
    }

    public Page<T> fetchPage(Pageable pageable, SessionModel sessionModel, T condition, Object query) {
        BooleanBuilder builder = this.generateCommenCondition(query, condition);
        Page<T> page = this.repository.findAll(builder, pageable);
        return page;
    }

    public T add(BaseEntity qEntity, SessionModel sessionModel){
        T newEntity = this.generateEntity(qEntity,sessionModel);
        return this.repository.save(newEntity);
    }

    public void update(BaseEntity update,SessionModel session){
        Optional<T> optional = this.repository.findById(update.getId());
        if(!optional.isPresent()){
            log.info("更新的对象不存在");
            throw new BadRequestException("", "更新的对象不存在");
        }
        T old = optional.get();
        BeanUtils.copyProperties(update,old,this.getGenericClass());
        this.repository.save(old);
    }

    public void delete(Long id){
        this.repository.deleteById(id);
    }
}
